///////////////////////////////////////////////////////////////////////////////
// Filename: DirectX_10_Sound.cpp
///////////////////////////////////////////////////////////////////////////////
#include "DirectX_10_Sound.h"
#include "DirectX_10_Utils.h"

namespace Omen {

DirectX_10_Sound::DirectX_10_Sound()
{
	m_DirectSound = 0;
	m_primaryBuffer = 0;
	m_secondaryBuffer1 = 0;
}


DirectX_10_Sound::DirectX_10_Sound(const DirectX_10_Sound& other)
{
}


DirectX_10_Sound::~DirectX_10_Sound()
{
}


bool DirectX_10_Sound::Initialize(HWND hwnd)
{
	bool result;


	// Initialize direct sound and the primary sound buffer.
	result = InitializeDirectSound(hwnd);
	if(!result)
	{
		return false;
	}

	// Load a Mp3 audio file onto a secondary buffer.
	result = LoadMp3File(DATA_PATH("data"), &m_secondaryBuffer1);
	if(!result)
	{
		return false;
	}

	// Play the Mp3 file now that it has been loaded.
	result = PlayMp3File();
	if(!result)
	{
		return false;
	}

	return true;
}


void DirectX_10_Sound::Shutdown()
{
	// Release the secondary buffer.
	ShutdownMp3File(&m_secondaryBuffer1);

	// Shutdown the Direct Sound API.
	ShutdownDirectSound();

	return;
}


bool DirectX_10_Sound::InitializeDirectSound(HWND hwnd)
{
	HRESULT result;
	DSBUFFERDESC bufferDesc;
	WAVEFORMATEX Mp3Format;


	// Initialize the direct sound interface pointer for the default sound device.
	result = DirectSoundCreate8(NULL, &m_DirectSound, NULL);
	if(FAILED(result))
	{
		return false;
	}

	// Set the cooperative level to priority so the format of the primary sound buffer can be modified.
	result = m_DirectSound->SetCooperativeLevel(hwnd, DSSCL_PRIORITY);
	if(FAILED(result))
	{
		return false;
	}

	// Setup the primary buffer description.
	bufferDesc.dwSize = sizeof(DSBUFFERDESC);
	bufferDesc.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRLVOLUME;
	bufferDesc.dwBufferBytes = 0;
	bufferDesc.dwReserved = 0;
	bufferDesc.lpwfxFormat = NULL;
	bufferDesc.guid3DAlgorithm = GUID_NULL;

	// Get control of the primary sound buffer on the default sound device.
	result = m_DirectSound->CreateSoundBuffer(&bufferDesc, &m_primaryBuffer, NULL);
	if(FAILED(result))
	{
		return false;
	}

	// Setup the format of the primary sound bufffer.
	// In this case it is a .WAV file recorded at 44,100 samples per second in 16-bit stereo (cd audio format).
	Mp3Format.wFormatTag = WAVE_FORMAT_PCM;
	Mp3Format.nSamplesPerSec = 44100;
	Mp3Format.wBitsPerSample = 16;
	Mp3Format.nChannels = 2;
	Mp3Format.nBlockAlign = (Mp3Format.wBitsPerSample / 8) * Mp3Format.nChannels;
	Mp3Format.nAvgBytesPerSec = Mp3Format.nSamplesPerSec * Mp3Format.nBlockAlign;
	Mp3Format.cbSize = 0;

	// Set the primary buffer to be the Mp3 format specified.
	result = m_primaryBuffer->SetFormat(&Mp3Format);
	if(FAILED(result))
	{
		return false;
	}

	return true;
}


void DirectX_10_Sound::ShutdownDirectSound()
{
	// Release the primary sound buffer pointer.
	if(m_primaryBuffer)
	{
		m_primaryBuffer->Release();
		m_primaryBuffer = 0;
	}

	// Release the direct sound interface pointer.
	if(m_DirectSound)
	{
		m_DirectSound->Release();
		m_DirectSound = 0;
	}

	return;
}


bool DirectX_10_Sound::LoadMp3File(char* filename, IDirectSoundBuffer8** secondaryBuffer)
{
	int error;
	FILE* filePtr;
	unsigned int count;
	Mp3HeaderType Mp3FileHeader;
	WAVEFORMATEX Mp3Format;
	DSBUFFERDESC bufferDesc;
	HRESULT result;
	IDirectSoundBuffer* tempBuffer;
	unsigned char* Mp3Data;
	unsigned char* bufferPtr;
	unsigned long bufferSize;


	// Open the Mp3 file in binary.
	error = fopen_s(&filePtr, filename, "rb");
	if(error != 0)
	{
		return false;
	}

	// Read in the Mp3 file header.
	count = fread(&Mp3FileHeader, sizeof(Mp3FileHeader), 1, filePtr);
	if(count != 1)
	{
		return false;
	}

	// Check that the chunk ID is the RIFF format.
	if((Mp3FileHeader.chunkId[0] != 'R') || (Mp3FileHeader.chunkId[1] != 'I') || 
	   (Mp3FileHeader.chunkId[2] != 'F') || (Mp3FileHeader.chunkId[3] != 'F'))
	{
		return false;
	}

	// Check that the file format is the Mp3 format.
	if((Mp3FileHeader.format[0] != 'W') || (Mp3FileHeader.format[1] != 'A') ||
	   (Mp3FileHeader.format[2] != 'V') || (Mp3FileHeader.format[3] != 'E'))
	{
		return false;
	}

	// Check that the sub chunk ID is the fmt format.
	if((Mp3FileHeader.subChunkId[0] != 'f') || (Mp3FileHeader.subChunkId[1] != 'm') ||
	   (Mp3FileHeader.subChunkId[2] != 't') || (Mp3FileHeader.subChunkId[3] != ' '))
	{
		return false;
	}

	// Check that the audio format is WAVE_FORMAT_PCM.
	if(Mp3FileHeader.audioFormat != WAVE_FORMAT_PCM)
	{
		return false;
	}

	// Check that the Mp3 file was recorded in stereo format.
	if(Mp3FileHeader.numChannels != 2)
	{
		return false;
	}

	// Check that the Mp3 file was recorded at a sample rate of 44.1 KHz.
	if(Mp3FileHeader.sampleRate != 44100)
	{
		return false;
	}

	// Ensure that the Mp3 file was recorded in 16 bit format.
	if(Mp3FileHeader.bitsPerSample != 16)
	{
		return false;
	}

	// Check for the data chunk header.
	if((Mp3FileHeader.dataChunkId[0] != 'd') || (Mp3FileHeader.dataChunkId[1] != 'a') ||
	   (Mp3FileHeader.dataChunkId[2] != 't') || (Mp3FileHeader.dataChunkId[3] != 'a'))
	{
		return false;
	}

	// Set the Mp3 format of secondary buffer that this Mp3 file will be loaded onto.
	Mp3Format.wFormatTag = WAVE_FORMAT_PCM;
	Mp3Format.nSamplesPerSec = 44100;
	Mp3Format.wBitsPerSample = 16;
	Mp3Format.nChannels = 2;
	Mp3Format.nBlockAlign = (Mp3Format.wBitsPerSample / 8) * Mp3Format.nChannels;
	Mp3Format.nAvgBytesPerSec = Mp3Format.nSamplesPerSec * Mp3Format.nBlockAlign;
	Mp3Format.cbSize = 0;

	// Set the buffer description of the secondary sound buffer that the Mp3 file will be loaded onto.
	bufferDesc.dwSize = sizeof(DSBUFFERDESC);
	bufferDesc.dwFlags = DSBCAPS_CTRLVOLUME;
	bufferDesc.dwBufferBytes = Mp3FileHeader.dataSize;
	bufferDesc.dwReserved = 0;
	bufferDesc.lpwfxFormat = &Mp3Format;
	bufferDesc.guid3DAlgorithm = GUID_NULL;

	// Create a temporary sound buffer with the specific buffer settings.
	result = m_DirectSound->CreateSoundBuffer(&bufferDesc, &tempBuffer, NULL);
	if(FAILED(result))
	{
		return false;
	}

	// Test the buffer format against the direct sound 8 interface and create the secondary buffer.
	result = tempBuffer->QueryInterface(IID_IDirectSoundBuffer8, (void**)&*secondaryBuffer);
	if(FAILED(result))
	{
		return false;
	}

	// Release the temporary buffer.
	tempBuffer->Release();
	tempBuffer = 0;

	// Move to the beginning of the Mp3 data which starts at the end of the data chunk header.
	fseek(filePtr, sizeof(Mp3HeaderType), SEEK_SET);

	// Create a temporary buffer to hold the Mp3 file data.
	Mp3Data = new unsigned char[Mp3FileHeader.dataSize];
	if(!Mp3Data)
	{
		return false;
	}

	// Read in the Mp3 file data into the newly created buffer.
	count = fread(Mp3Data, 1, Mp3FileHeader.dataSize, filePtr);
	if(count != Mp3FileHeader.dataSize)
	{
		return false;
	}

	// Close the file once done reading.
	error = fclose(filePtr);
	if(error != 0)
	{
		return false;
	}

	// Lock the secondary buffer to write Mp3 data into it.
	result = (*secondaryBuffer)->Lock(0, Mp3FileHeader.dataSize, (void**)&bufferPtr, (DWORD*)&bufferSize, NULL, 0, 0);
	if(FAILED(result))
	{
		return false;
	}

	// Copy the Mp3 data into the buffer.
	memcpy(bufferPtr, Mp3Data, Mp3FileHeader.dataSize);

	// Unlock the secondary buffer after the data has been written to it.
	result = (*secondaryBuffer)->Unlock((void*)bufferPtr, bufferSize, NULL, 0);
	if(FAILED(result))
	{
		return false;
	}
	
	// Release the Mp3 data since it was copied into the secondary buffer.
	delete [] Mp3Data;
	Mp3Data = 0;

	return true;
}


void DirectX_10_Sound::ShutdownMp3File(IDirectSoundBuffer8** secondaryBuffer)
{
	// Release the secondary sound buffer.
	if(*secondaryBuffer)
	{
		(*secondaryBuffer)->Release();
		*secondaryBuffer = 0;
	}

	return;
}


bool DirectX_10_Sound::PlayMp3File()
{
	HRESULT result;


	// Set position at the beginning of the sound buffer.
	result = m_secondaryBuffer1->SetCurrentPosition(0);
	if(FAILED(result))
	{
		return false;
	}

	// Set volume of the buffer to 100%.
	result = m_secondaryBuffer1->SetVolume(DSBVOLUME_MAX);
	if(FAILED(result))
	{
		return false;
	}

	// Play the contents of the secondary sound buffer.
	result = m_secondaryBuffer1->Play(0, 0, 0);
	if(FAILED(result))
	{
		return false;
	}

	return true;
}

} // namespace Omen

// EOF